/* 
 * File:   test.cpp
 * Author: RaF
 *
 * Created on 26 novembre 2012, 8.59
 */

#include <cstdlib>
#include <iostream>
#include "SimpleGraph.hpp"
#include "Graph.h"
#include "Vertex.hpp"
#include "Edge.hpp"
#include "DirectedGraph.hpp"
#include <string>
#include "ShortestPath.hpp"
#include "DijkstraInfo.hpp"

using namespace std;

template <typename T> void printVertices(std::vector<Vertex<T>*>* vector);
template <typename T> void printEdges(std::vector<Edge<T>*>* vector);
template <typename T> void testGraph(Graph<T> *g, bool isSimple);
template <typename T> void testKruskal(SimpleGraph<T> *g);
template <typename T> void testDijkstra(DirectedGraph<T>* g);
template <typename T> void printDijkstraResult(vector<DijkstraInfo<T>*>* res, Vertex<T>* source);
template <typename I> void printVertexInfo(Vertex<I>* v) {
    cout << "v_info: " + v->getInfo() << endl;
}

/*
 * test sulla gestione dei grafi
 */
int main(int argc, char** argv) {
    try {
        Graph<string> *g = new SimpleGraph<string > ();

        cout << "TESTING SIMPLE GRAPH" << "\n";
        testGraph(g, true);
        delete g;

        g = new DirectedGraph<string > ();
        cout << "TESTING DIRECTED GRAPH" << "\n";
        testGraph(g, false);
        
        
        delete g;

    } catch (const std::exception& ex) {
        cout << ex.what() << endl;
    }

    
    

    /*
    std::map<int, VisitInfo> prova;
    VisitInfo info(WHITE,0);
    prova.insert(std::pair<int, VisitInfo > (1, info));
    cout << prova.at(1).getColor() << endl;
    prova.at(1).setColor(BLACK);
    cout << prova.at(1).getColor() << endl;
     */
    return 0;
}

template <typename T> void testGraph(Graph<T> *g, bool isSimple) {
    string s1 = "ciao ";
    string s2 = " a ";
    string s3 = "tutti";

    //test inserimento vertici
    cout << "\nTest insertVertex" << endl;
    g->insertVertex(s1);
    g->insertVertex(s2);
    g->insertVertex(s3);

    //stampa vertici
    std::vector<Vertex<string>*>* vertexVector = g->getVertices();
    printVertices(vertexVector);

    //test inserimento archi
    cout << "\nTest insertEdge" << endl;
    g->insertEdge(vertexVector->at(0), vertexVector->at(1), 1);
    g->insertEdge(vertexVector->at(1), vertexVector->at(2), 10);
    g->insertEdge(vertexVector->at(2), vertexVector->at(0), 2);

    //stampa archi
    std::vector<Edge<string>*>* edgeVector = g->getEdges();
    printEdges(edgeVector);

    //test areAdjacent
    cout << "\nTest areAdjacent" << endl;
    Vertex<string>* v = vertexVector->at(0);
    Vertex<string>* u = vertexVector->at(1);
    string mess = "il vertice:" + v->getInfo() + " e il vertice:" + u->getInfo();
    if (g->areAdjacent(v, u))
        cout << mess + " sono adiacenti" << endl;
    else
        cout << mess + " non sono adiacenti" << endl;

    //test visitByDFS
    cout << "\nTest visitByDFS" << endl;
    //esegue la visita per profondità, eseguendo la printVertexInfo su ogni vertice visitato
    std::map<Vertex<string>*, Vertex<string>*>* parentMap;
    parentMap = g->VisitByDFS(printVertexInfo);
    //stampo la mappa figlio/padre, ottenuta dagli alberi risultato della visità in profondità
    std::map<Vertex<string>*, Vertex<string>*>::iterator it;
    for (it = parentMap->begin(); it != parentMap->end(); ++it) {
        cout << "vertex: " + (it->first)->getInfo();
        if ((it->second) != NULL)
            cout << "\tfather: " + (it->second)->getInfo() << endl;
        else
            cout << "\tfather: NULL" << endl;

    }

    //test getOpposite
    cout << "\nTest getOpposite" << endl;
    Vertex<string>* opposite = g->getOpposite(v, edgeVector->at(0));
    if (opposite != NULL)
        cout << "il vertice:" + v->getInfo() + "ha come opposto sull'arco 0:" + opposite->getInfo() << endl;

    if (isSimple) {
        cout << "\nTest Kruskal" << endl;
        delete edgeVector;
        SimpleGraph<string> *sg = dynamic_cast<SimpleGraph<string>*> (g);
        edgeVector = sg->kruskal();

        //stampa archi
        printEdges(edgeVector);
    }
    
    if(!isSimple){
        cout << "\nTESTING DIJKSTRA" << "\n";
        DirectedGraph<string>* dg = dynamic_cast<DirectedGraph<string>*>(g);
        testDijkstra(dg);
    }

    //test removeEdge
    cout << "\nTest removeEdge" << endl;
    g->removeEdge(g->getEdges()->at(1));
    cout << "\nRemoved edge 1" << endl;
    printEdges(g->getEdges());

    //test removeVertex
    cout << "\nTest removeVertex" << endl;
    g->removeVertex(g->getVertices()->at(1));
    cout << "\nRemoved vertex 0" << endl;
    g->removeVertex(g->getVertices()->at(0));
    cout << "\nRemoved vertex 0" << endl;
    printVertices(g->getVertices());
    printEdges(g->getEdges());


    //memory free
    delete edgeVector;
    delete vertexVector;
    delete parentMap;
}

template <typename T> void printVertices(std::vector<Vertex<T>*>* vector) {
    cout << "\n*********VERTICES*********\n";
    for (int i = 0; i < vector->size(); i++) {
        cout << i << ": " << (vector->at(i))->getInfo() << endl;
        //cout << (vertexVector->at(i))->getLabel();
    }
    cout << endl;
}

template <typename T> void printEdges(std::vector<Edge<T>*>* vector) {
    cout << "\n*********EDGES*********\n";
    for (int i = 0; i < vector->size(); i++) {
        //cout << (edgeVector->at(i))->getWeight();
        cout << i << ": FROM: " << (vector->at(i))->getFrom()->getInfo();
        cout << "\tTO: " << (vector->at(i))->getTo()->getInfo();
        cout << "\tW: " << vector->at(i)->getWeight() << endl;
    }
    cout << endl;
}

template <typename T> void testDijkstra(DirectedGraph<T>* g){
    int sourceIndex=0;
    Vertex<T>* source=g->getVertices()->at(sourceIndex);
    vector<DijkstraInfo<T>*>* result=ShortestPath<T>::dijkstra(g,source);
    printDijkstraResult(result,source);    
}

template <typename T> void printDijkstraResult(vector<DijkstraInfo<T>*>* res, Vertex<T>* source){
     cout << "\n*********SHORTEST PATHS*********\n";
     cout << "Source: " << source->getInfo();
    typename std::vector<DijkstraInfo<T>*>::iterator itResult;
    int i = 0;
    for(itResult=res->begin(); itResult != res->end(); ++itResult){
         cout << i << " vertex: " << (*itResult)->getCurrent()->getInfo();
         cout << " ->distance from source " << (*itResult)->getDistance() << endl;
         i++;
    }
}